# ~*~ coding: utf-8 ~*~
import traceback

from flask import current_app, render_template, redirect, url_for
from flask_login import current_user, login_required

from apps.extensions import db, csrf
from apps.common.response import permissions_error, server_error
from apps.common.request import is_ajax
from .models import UserRole, Role, Permission, RolePermission
from .menu import build_menu


class PermissionMixin(object):
    @property
    def all_roles(self):
        """获取当前用户的所有角色"""
        roles = db.session.query(Role).join(UserRole, Role.id == UserRole.role_id). \
            filter(UserRole.user_id == self.id).all()
        return roles

    @property
    def all_permissions(self):
        """获取当前用户的所有权限"""
        role_ids = []
        for role in self.all_roles:
            role_ids.append(role.id)

        permissions = db.session.query(Permission).\
            join(RolePermission, Permission.id == RolePermission.permission_id).\
            filter(RolePermission.role_id.in_(role_ids)).\
            filter(Permission.is_menu == 0).\
            all()
        return set(permissions)     # 角色中可能存在相同的权限

    def has_perm(self, perm):
        if self.is_admin:
            return True

        permission_list = []
        for permission in self.all_permissions:
            permission_list.append('{}-{}-{}'.format(permission.module, permission.controller, permission.action))

        return perm in permission_list

    @property
    def menus(self):
        if self.is_admin:
            perm_list = db.session.query(Permission).\
                filter(Permission.is_menu == 1). \
                order_by(Permission.sort.asc()). \
                order_by(Permission.create_time.asc()). \
                all()
        else:
            role_ids = [role.id for role in self.all_roles]
            perm_list = db.session.query(Permission).\
                join(RolePermission, Permission.id == RolePermission.permission_id).\
                filter(RolePermission.role_id.in_(role_ids), Permission.is_menu == 1). \
                order_by(Permission.sort.asc()). \
                order_by(Permission.create_time.asc()). \
                all()

        return build_menu(perm_list, navigation=True)


class PermissionRequiredMixin(object):
    permission_required = None

    def has_perm(self):
        return current_user.has_perm(self.permission_required)

    def has_object_perm(self, *args, **kwargs):
        return True

    def has_otp_perm(self, *args, **kwargs):
        # 被强制开启otp并且用户没有进行验证
        if current_user.otp_force_enabled and not current_user.otp_turned_on:
            return False
        return True

    def dispatch_request(self, *args, **kwargs):
        if 'pk' in kwargs:
            kwargs['pk'] = kwargs.get('pk').__str__()

        if not self.has_otp_perm(*args, **kwargs):
            return render_template('_otp.html')

        if self.has_perm() and self.has_object_perm(*args, **kwargs):
            try:
                return super(PermissionRequiredMixin, self).dispatch_request(*args, **kwargs)
            except Exception as e:
                current_app.logger.error(traceback.format_exc())
                if not current_app.config.get('DEBUG'):
                    if is_ajax():
                        return server_error({
                            'message': '网站出现未知错误，请联系管理员！'
                        })
                    else:
                        return render_template('500.html')
                else:
                    raise e
        else:
            if is_ajax():
                return permissions_error({
                    'message': '您没有权限访问该资源！',
                    'type': 'permission'
                })
            else:
                return render_template('403.html')


class LoginRequiredMixin(object):
    decorators = [login_required]


class CsrfExemptMixin(object):
    decorators = [csrf.exempt]


class LoginRequiredAndCsrfExemptMixin(object):
    decorators = [login_required, csrf.exempt]
